﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using Radiation___IM;
using Radiation.Common;
using System.Windows.Forms;

namespace RadiationUI
{
    public class Connection
    {
        public static TcpClient Client;
        public static string IP;
        public static int Port;
        public static string Nickname = "Invité";
        public const int rcv_buffer_size = 65535;

        public static event EventHandler<GenericEventArgs> NetworkDataReceived;
        public static event EventHandler<GenericEventArgs> NetworkDataSent;
        public static event EventHandler<GenericEventArgs> NicknameModified;
        public static event EventHandler<GenericEventArgs> FriendAdded;
        public static event EventHandler<GenericEventArgs> FriendRemoved;
        public static event EventHandler<GenericEventArgs> FriendListCleared;
        public static event EventHandler<GenericEventArgs> FriendListRefreshed;
        public static event EventHandler<GenericEventArgs> ServerVersionReceived;

        public static void sendToAPI(string api_command)
        {
            sendAsync("[channel=default;c=0]/api " + api_command);
        }

        public static void sendAsync(string to_send)
        {
            try
            {
                byte[] message = UnicodeEncoding.Unicode.GetBytes(AESManager.Instance.Encrypt(to_send));

                try
                {
                    Connection.Client.Client.BeginSend(message, 0, message.Length, System.Net.Sockets.SocketFlags.None, endAsyncSend, to_send);
                    OnNetworkDataSent(to_send);
                }
                catch (Exception e)
                {
                    // Begin Send failed
                    Logger.Instance["error"].add("Connection.sendAsync : Begin send failed ! (" + e.ToString() + ")");
                }
            }
            catch (Exception e)
            {
                // Encryption failed
                Logger.Instance["error"].add("Connection.sendAsync : Encryption failed ! (" + e.ToString() + ")");
            }
        }

        public static void endAsyncSend(IAsyncResult result)
        {
            try
            {
                Connection.Client.Client.EndSend(result);
            }
            catch (Exception)
            {
                // EndSend failed ..
            }
        }

        /// <summary>
        /// Init the connection
        /// </summary>
        /// <returns>True if an error happened, otherwise false</returns>
        public static bool initConnection()
        {
            byte[] buffer = new byte[65535];
            int read = Connection.Client.Client.Receive(buffer);
            RSAManager.Instance.m_server_public_key = UnicodeEncoding.Unicode.GetString(buffer, 0, read);
            Connection.Client.Client.Send(UnicodeEncoding.Unicode.GetBytes(RSAManager.Instance.XMLPublicKey));

            read = Connection.Client.Client.Receive(buffer);
            string text = RSAManager.Instance.Decrypt(UnicodeEncoding.Unicode.GetString(buffer, 0, read));

            if (text != "aes.getkey")
            {
                MessageBox.Show("Une erreure est survenue durant la sécurisation de votre connection [#AES-KEY]");
                return true;
            }

            Connection.Client.Client.Send(UnicodeEncoding.Unicode.GetBytes(RSAManager.Instance.Encrypt(Convert.ToBase64String(AESManager.Instance.Key))));

            read = Connection.Client.Client.Receive(buffer);
            text = RSAManager.Instance.Decrypt(UnicodeEncoding.Unicode.GetString(buffer, 0, read));

            if (text != "aes.getiv")
            {
                MessageBox.Show("Une erreure est survenue durant la sécurisation de votre connection [#AES-IV]");
                return true;
            }

            Connection.Client.Client.Send(UnicodeEncoding.Unicode.GetBytes(RSAManager.Instance.Encrypt(Convert.ToBase64String(AESManager.Instance.IV))));

            byte[] receive_buffer = new byte[Connection.rcv_buffer_size];
            Connection.Client.Client.BeginReceive(receive_buffer, 0, Connection.rcv_buffer_size, System.Net.Sockets.SocketFlags.None, Connection.handleRead, receive_buffer);

            return false;
        }

        public static void handleRead(IAsyncResult result)
        {
            try
            {
                byte[] buffer = (byte[])result.AsyncState;
                int read = Connection.Client.Client.EndReceive(result);

                string[] commands = AESManager.Instance.Decrypt(UnicodeEncoding.Unicode.GetString(buffer, 0, read));

                for (int k = 0; k < commands.Length; k++)
                {
                    if (commands[k] != null && commands[k].Length > 0)
                    {
                        Radiation___IM.MessageParser parser = new Radiation___IM.MessageParser();
                        parser.parse(commands[k]);

                        OnNetworkActivity(parser.OriginalMessage);

                        if ((string)parser.Parameters["a"] == "1")
                        {
                            // API Command repsonse
                            bool validApiCommand = false;

                            switch ((string)parser.Parameters["ap"])
                            {
                                case "user.nickname":
                                    Connection.Nickname = parser.Message;
                                    OnNicknameModified(Connection.Nickname);
                                    break;
                                case Radiation.Common.Communication.APIMethods.LOGIN:
                                    validApiCommand = true; // a form is waiting it 
                                    break;
                                case Radiation.Common.Communication.APIMethods.GET_CHANNEL_USERS:
                                    {
                                        string[] row_thing = parser.Message.Split(new char[] { ';' });

                                        if (row_thing.Length > 1 && WindowManager.Instance[row_thing[0]] != null)
                                        {
                                            string[] users = new string[row_thing.Length - 1];
                                            for (int i = 0, j = row_thing.Length - 1; i < j; i++)
                                                users[i] = row_thing[i + 1];
                                            WindowManager.Instance[row_thing[0]].addUsers(users);
                                        }
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_ADD_USER:
                                    {
                                        string[] row_thing = parser.Message.Split(new char[] { ';' });

                                        if (row_thing.Length > 1 && WindowManager.Instance[row_thing[0]] != null)
                                            WindowManager.Instance[row_thing[0]].addUsers(row_thing[1]);
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_REMOVE_USER:
                                    {
                                        string[] row_thing = parser.Message.Split(new char[] { ';' });

                                        if (row_thing.Length > 1 && WindowManager.Instance[row_thing[0]] != null)
                                            WindowManager.Instance[row_thing[0]].removeUsers(row_thing[1]);
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_RENAME_USER:
                                    {
                                        string[] row_thing = parser.Message.Split(new char[] { ';' });

                                        if (row_thing.Length > 2 && WindowManager.Instance[row_thing[0]] != null)
                                            WindowManager.Instance[row_thing[0]].renameUsers(row_thing[1], row_thing[2]);
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.ADD_FRIEND:
                                    {
                                        validApiCommand = true;
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_ADD_FRIEND:
                                    {
                                        OnFriendAdded(parser.Message);
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_REMOVE_FRIEND:
                                    {
                                        OnFriendRemoved(parser.Message);
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CLIENT_CLEAR_FRIEND_LIST:
                                    {
                                        OnFriendListCleared();
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.GET_FRIEND_LIST:
                                    {
                                        OnFriendListRefreshed(parser.Message.Split(new char[] { ';' }));
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.CREATE_ACCOUNT:
                                    {
                                        validApiCommand = true;
                                    }
                                    break;
                                case Radiation.Common.Communication.APIMethods.GET_SERVER_VERSION:
                                    {
                                        OnServerVersionReceived(parser.Message);
                                    }
                                    break;
                            }

                            if (validApiCommand)
                            {
                                AsyncAPI.Instance.signal(parser);
                            }
                        }
                        else
                        {
                            if (WindowManager.Instance[(string)parser.Parameters["channel"]] != null)
                            {
                                WindowManager.Instance[(string)parser.Parameters["channel"]].addMessage((string)parser.Parameters["user"],
                                    parser.Message);
                            }
                            else
                                MessageBox.Show((string)parser.Parameters["channel"]);
                        }
                    }
                }

                byte[] receive_buffer = new byte[rcv_buffer_size];
                Connection.Client.Client.BeginReceive(receive_buffer, 0, rcv_buffer_size, System.Net.Sockets.SocketFlags.None, handleRead, receive_buffer);
            }
            catch (Exception)
            {
                try
                {
                    byte[] receive_buffer = new byte[rcv_buffer_size];
                    Connection.Client.Client.BeginReceive(receive_buffer, 0, rcv_buffer_size, System.Net.Sockets.SocketFlags.None, handleRead, receive_buffer);
                }
                catch (Exception)
                {
                    // ... 
                    MessageBox.Show("La connexion entre vous et le serveur à été coupée.", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        protected static void OnNetworkActivity(string message)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = message;
            if (NetworkDataReceived != null)
                NetworkDataReceived(e, e);
        }

        protected static void OnNetworkDataSent(string message)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = message;
            if (NetworkDataSent != null)
                NetworkDataSent(e, e);
        }

        protected static void OnNicknameModified(string message)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = message;
            if (NicknameModified != null)
                NicknameModified(e, e);
        }

        protected static void OnFriendAdded(string friend)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = friend;
            if (FriendAdded != null)
                FriendAdded(e, e);
        }

        protected static void OnFriendRemoved(string friend)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = friend;
            if (FriendRemoved != null)
                FriendRemoved(e, e);
        }

        protected static void OnFriendListCleared()
        {
            GenericEventArgs e = new GenericEventArgs();
            if (FriendListCleared != null)
                FriendListCleared(e, e);
        }

        protected static void OnFriendListRefreshed(string[] friends)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = friends;
            if (FriendListRefreshed != null)
                FriendListRefreshed(e, e);
        }

        protected static void OnServerVersionReceived(string version)
        {
            GenericEventArgs e = new GenericEventArgs();
            e["message"] = version;
            if (ServerVersionReceived != null)
                ServerVersionReceived(e, e);
        }
    }
}
